import { message } from 'antd'
import { setRange } from '../utils'

export function useInput(send: (text: string) => void) {
  const ref = useRef<HTMLDivElement>(null)

  // 清空文本
  const clearText = useCallback(() => {
    if (ref.current) {
      ref.current.innerHTML = ''
      setRange(ref.current, 0)
    }
  }, [])

  // 向文本框中插入内容
  const insertText = useCallback((text: string) => {
    const range = window.getSelection()?.getRangeAt(0) // 当前的选区或文本框焦点的第一个区域（在非 Firefox 里只会有一个区域）
    const input = ref.current // 输入框
    if (!range || !input) return

    const selectionNode = range.startContainer
    const offset = range.startOffset
    const nodeText = selectionNode.textContent ?? ''

    // 光标处于文本中间时，在光标处插入文本；否则直接在末尾附加文本
    if (
      selectionNode.parentElement === input && // selectionNode 的父元素是 input 代表文本框里有内容，且焦点在文本框中
      !(selectionNode === input.lastChild && offset === nodeText.length) // 确定光标不处于文本框结尾
    ) {
      const replaceNode = document.createTextNode(
        nodeText.slice(0, offset) + text + nodeText.slice(offset),
      )
      input.replaceChild(replaceNode, selectionNode)
      setRange(replaceNode, offset + text.length) // 还原光标位置（不然光标会自动跑到文本框末尾）
    } else {
      if (text === '\n' && !input.innerHTML.endsWith('\n')) text = '\n\n' // 向文本框末尾添加回车时，需要添加两个才能让光标出现在下一行（浏览器默认行为也是这样）
      input.innerHTML += text
      setRange(input, input.childNodes.length) // 把光标定位到输入框末尾
    }
  }, [])

  const triggerSend = useCallback(() => {
    let value = ref.current?.innerText ?? ''
    if (!value.length) return
    if (value.length > 5000) {
      // 目前微信发送消息上限是 16382 个字符，一个中文算 3 个字符
      value = value.slice(0, 5000)
      void message.warning('只能发送 5000 字以内的消息')
    }

    send(value)
    clearText()
  }, [send])

  // 处理回车
  const onKeyPress = useCallback(
    (e: React.KeyboardEvent<HTMLDivElement>) => {
      const isEnter = e.key === 'Enter' || e.code === 'Enter' // 部分浏览器里按回车，key 是 '\n'，code 才是 Enter
      if (!isEnter) return

      // Ctrl + Enter：插入换行
      if (e.ctrlKey) return void insertText('\n')
      // Shift + Enter 的行为也是插入换行，不过浏览器会自动插入，不需要执行操作
      if (e.shiftKey) return

      // Enter 发送消息
      e.preventDefault()
      triggerSend()
    },
    [insertText, triggerSend],
  )

  return {
    ref,
    insertText,
    onKeyPress,
    triggerSend,
  }
}
